home *** CD-ROM | disk | FTP | other *** search
/ Programmer Power Tools / Programmer Power Tools.iso / modem / rzsz2.arc / RZ.C < prev    next >
C/C++ Source or Header  |  1987-12-08  |  26KB  |  1,230 lines

  1. #define VERSION "1.26 10-11-87"
  2. #define PUBDIR "/usr/spool/uucppublic"
  3.  
  4. /*% cc -M0 -Ox -K -i % -o rz; size rz;
  5. <-xtx-*> cc386 -Ox rz.c -o $B/rz;  size $B/rz
  6.  *
  7.  * rz.c By Chuck Forsberg
  8.  *
  9.  *    cc -O rz.c -o rz        USG (3.0) Unix
  10.  *     cc -O -DV7  rz.c -o rz        Unix V7, BSD 2.8 - 4.3
  11.  *
  12.  *    ln rz rb;  ln rz rx            For either system
  13.  *
  14.  *    ln rz /usr/bin/rzrmail        For remote mail.  Make this the
  15.  *                    login shell. rzrmail then calls
  16.  *                    rmail(1) to deliver mail.
  17.  *
  18.  *
  19.  *  Unix is a trademark of Western Electric Company
  20.  *
  21.  * A program for Unix to receive files and commands from computers running
  22.  *  Professional-YAM, PowerCom, YAM, IMP, or programs supporting XMODEM.
  23.  *  rz uses Unix buffered input to reduce wasted CPU time.
  24.  *
  25.  * Iff the program is invoked by rzCOMMAND, output is piped to 
  26.  * "COMMAND filename"
  27.  *
  28.  *  Some systems (Venix, Coherent, Regulus) may not support tty raw mode
  29.  *  read(2) the same way as Unix. ONEREAD must be defined to force one
  30.  *  character reads for these systems. Added 7-01-84 CAF
  31.  *
  32.  *  Alarm signal handling changed to work with 4.2 BSD 7-15-84 CAF 
  33.  *
  34.  *  BIX added 6-30-87 to support BIX(TM) upload protocol used by the
  35.  *  Byte Information Exchange.
  36.  *
  37.  *  NFGVMIN Updated 2-18-87 CAF for Xenix systems where c_cc[VMIN]
  38.  *  doesn't work properly (even though it compiles without error!),
  39.  *
  40.  *  HOWMANY may be tuned for best performance
  41.  *
  42.  *  USG UNIX (3.0) ioctl conventions courtesy  Jeff Martin
  43.  */
  44. #define LOGFILE "/tmp/rzlog"
  45.  
  46. #include <stdio.h>
  47. #include <signal.h>
  48. #include <setjmp.h>
  49. #include <ctype.h>
  50. FILE *popen();
  51.  
  52. #define OK 0
  53. #define FALSE 0
  54. #define TRUE 1
  55. #define ERROR (-1)
  56.  
  57. /*
  58.  * Max value for HOWMANY is 255.
  59.  *   A larger value reduces system overhead but may evoke kernel bugs.
  60.  *   133 corresponds to an XMODEM/CRC sector
  61.  */
  62. #ifndef HOWMANY
  63. #define HOWMANY 133
  64. #endif
  65.  
  66. int Zmodem=0;        /* ZMODEM protocol requested */
  67. int Nozmodem = 0;    /* If invoked as "rb" */
  68. unsigned Baudrate;
  69. #include "rbsb.c"    /* most of the system dependent stuff here */
  70.  
  71. char *substr();
  72. FILE *fout;
  73.  
  74. /*
  75.  * Routine to calculate the free bytes on the current file system
  76.  *  ~0 means many free bytes (unknown)
  77.  */
  78. long getfree()
  79. {
  80.     return(~0L);    /* many free bytes ... */
  81. }
  82.  
  83. /* Ward Christensen / CP/M parameters - Don't change these! */
  84. #define ENQ 005
  85. #define CAN ('X'&037)
  86. #define XOFF ('s'&037)
  87. #define XON ('q'&037)
  88. #define SOH 1
  89. #define STX 2
  90. #define EOT 4
  91. #define ACK 6
  92. #define NAK 025
  93. #define CPMEOF 032
  94. #define WANTCRC 0103    /* send C not NAK to get crc not checksum */
  95. #define TIMEOUT (-2)
  96. #define RCDO (-3)
  97. #define ERRORMAX 5
  98. #define RETRYMAX 5
  99. #define WCEOT (-10)
  100. #define SECSIZ 128    /* cp/m's Magic Number record size */
  101. #define PATHLEN 257    /* ready for 4.2 bsd ? */
  102. #define KSIZE 1024    /* record size with k option */
  103. #define UNIXFILE 0x8000    /* happens to the the S_IFREG file mask bit for stat */
  104.  
  105. int Lastrx;
  106. int Crcflg;
  107. int Firstsec;
  108. int Eofseen;        /* indicates cpm eof (^Z) has been received */
  109. int errors;
  110. int Restricted=0;    /* restricted; no /.. or ../ in filenames */
  111. #ifdef ONEREAD
  112. /* Sorry, Regulus and some others don't work right in raw mode! */
  113. int Readnum = 1;    /* Number of bytes to ask for in read() from modem */
  114. #else
  115. int Readnum = HOWMANY;    /* Number of bytes to ask for in read() from modem */
  116. #endif
  117.  
  118. #define DEFBYTL 2000000000L    /* default rx file size */
  119. long Bytesleft;        /* number of bytes of incoming file left */
  120. long Modtime;        /* Unix style mod time for incoming file */
  121. short Filemode;        /* Unix style mode for incoming file */
  122. char Pathname[PATHLEN];
  123. char *Progname;        /* the name by which we were called */
  124.  
  125. int Batch=0;
  126. int Wcsmask=0377;
  127. int Topipe=0;
  128. int MakeLCPathname=TRUE;    /* make received pathname lower case */
  129. int Verbose=0;
  130. int Quiet=0;        /* overrides logic that would otherwise set verbose */
  131. int Nflag = 0;        /* Don't really transfer files */
  132. int Rxbinary=FALSE;    /* receive all files in bin mode */
  133. int Rxascii=FALSE;    /* receive files in ascii (translate) mode */
  134. int Thisbinary;        /* current file is to be received in bin mode */
  135. int Blklen;        /* record length of received packets */
  136. char secbuf[KSIZE+1];
  137. char linbuf[HOWMANY];
  138. int Lleft=0;        /* number of characters in linbuf */
  139. time_t timep[2];
  140. char Lzmanag;        /* Local file management request */
  141. char zconv;        /* ZMODEM file conversion request */
  142. char zmanag;        /* ZMODEM file management request */
  143. char ztrans;        /* ZMODEM file transport request */
  144. int Zctlesc;        /* Encode control characters */
  145. int Zrwindow = 1400;    /* RX window size (controls garbage count) */
  146.  
  147. jmp_buf tohere;        /* For the interrupt on RX timeout */
  148.  
  149. #include "zm.c"
  150.  
  151. int tryzhdrtype=ZRINIT;    /* Header type to send corresponding to Last rx close */
  152.  
  153. alrm()
  154. {
  155.     longjmp(tohere, -1);
  156. }
  157.  
  158. /* called by signal interrupt or terminate to clean things up */
  159. bibi(n)
  160. {
  161.     if (Zmodem)
  162.         zmputs(Attn);
  163.     canit(); mode(0);
  164.     fprintf(stderr, "rz: caught signal %d; exiting", n);
  165.     exit(128+n);
  166. }
  167.  
  168. main(argc, argv)
  169. char *argv[];
  170. {
  171.     register char *cp;
  172.     register npats;
  173.     char *virgin, **patts;
  174.     char *getenv();
  175.     int exitcode;
  176.  
  177.     Rxtimeout = 100;
  178.     setbuf(stderr, NULL);
  179.     if ((cp=getenv("SHELL")) && (substr(cp, "rsh") || substr(cp, "rksh")))
  180.         Restricted=TRUE;
  181.  
  182.     chkinvok(virgin=argv[0]);    /* if called as [-]rzCOMMAND set flag */
  183.     npats = 0;
  184.     while (--argc) {
  185.         cp = *++argv;
  186.         if (*cp == '-') {
  187.             while( *++cp) {
  188.                 switch(*cp) {
  189.                 case '+':
  190.                     Lzmanag = ZMAPND; break;
  191.                 case '1':
  192.                     iofd = 1; break;
  193.                 case '7':
  194.                     Wcsmask = 0177;
  195.                 case 'a':
  196.                     Rxascii=TRUE;  break;
  197.                 case 'b':
  198.                     Rxbinary=TRUE; break;
  199.                 case 'c':
  200.                     Crcflg=TRUE; break;
  201.                 case 'D':
  202.                     Nflag = TRUE; break;
  203.                 case 'e':
  204.                     Zctlesc = 1; break;
  205.                 case 'p':
  206.                     Lzmanag = ZMPROT;  break;
  207.                 case 'q':
  208.                     Quiet=TRUE; Verbose=0; break;
  209.                 case 't':
  210.                     if (--argc < 1) {
  211.                         usage();
  212.                     }
  213.                     Rxtimeout = atoi(*++argv);
  214.                     if (Rxtimeout<10 || Rxtimeout>1000)
  215.                         usage();
  216.                     break;
  217.                 case 'w':
  218.                     if (--argc < 1) {
  219.                         usage();
  220.                     }
  221.                     Zrwindow = atoi(*++argv);
  222.                     break;
  223.                 case 'u':
  224.                     MakeLCPathname=FALSE; break;
  225.                 case 'v':
  226.                     ++Verbose; break;
  227.                 default:
  228.                     usage();
  229.                 }
  230.             }
  231.         }
  232.         else if ( !npats && argc>0) {
  233.             if (argv[0][0]) {
  234.                 npats=argc;
  235.                 patts=argv;
  236.             }
  237.         }
  238.     }
  239.     if (npats > 1)
  240.         usage();
  241.     if (Batch && npats)
  242.         usage();
  243.     if (Verbose) {
  244.         if (freopen(LOGFILE, "a", stderr)==NULL) {
  245.             printf("Can't open log file %s\n",LOGFILE);
  246.             exit(0200);
  247.         }
  248.         setbuf(stderr, NULL);
  249.         fprintf(stderr, "argv[0]=%s Progname=%s\n", virgin, Progname);
  250.     }
  251.     if (fromcu() && !Quiet) {
  252.         if (Verbose == 0)
  253.             Verbose = 2;
  254.     }
  255.     mode(1);
  256.     if (signal(SIGINT, bibi) == SIG_IGN) {
  257.         signal(SIGINT, SIG_IGN); signal(SIGKILL, SIG_IGN);
  258.     }
  259.     else {
  260.         signal(SIGINT, bibi); signal(SIGKILL, bibi);
  261.     }
  262.     signal(SIGTERM, bibi);
  263.     if (wcreceive(npats, patts)==ERROR) {
  264.         exitcode=0200;
  265.         canit();
  266.     }
  267.     mode(0);
  268.     if (exitcode && !Zmodem)    /* bellow again with all thy might. */
  269.         canit();
  270.     exit(exitcode);
  271. }
  272.  
  273.  
  274. usage()
  275. {
  276.     fprintf(stderr,"%s %s for %s by Chuck Forsberg\n",
  277.       Progname, VERSION, OS);
  278.     fprintf(stderr,"Usage:    rz [-1abeuv]        (ZMODEM Batch)\n");
  279.     fprintf(stderr,"or    rb [-1abuv]        (YMODEM Batch)\n");
  280.     fprintf(stderr,"or    rx [-1abcv] file    (XMODEM or XMODEM-1k)\n");
  281.     fprintf(stderr,"      -1 For cu(1): Use fd 1 for input\n");
  282.     fprintf(stderr,"      -a ASCII transfer (strip CR)\n");
  283.     fprintf(stderr,"      -b Binary transfer for all files\n");
  284.     fprintf(stderr,"      -c Use 16 bit CRC    (XMODEM)\n");
  285.     fprintf(stderr,"      -e Escape control characters    (ZMODEM)\n");
  286.     fprintf(stderr,"      -v Verbose more v's give more info\n");
  287.     exit(1);
  288. }
  289. /*
  290.  *  Debugging information output interface routine
  291.  */
  292. /* VARARGS1 */
  293. vfile(f, a, b, c)
  294. register char *f;
  295. {
  296.     if (Verbose > 2) {
  297.         fprintf(stderr, f, a, b, c);
  298.         fprintf(stderr, "\n");
  299.     }
  300. }
  301.  
  302. /*
  303.  * Let's receive something already.
  304.  */
  305.  
  306. char *rbmsg =
  307. "%s ready. To begin transfer, type \"%s file ...\" to your modem program\r\n";
  308.  
  309. wcreceive(argc, argp)
  310. char **argp;
  311. {
  312.     register c;
  313.  
  314.     if (Batch || argc==0) {
  315.         Crcflg=(Wcsmask==0377);
  316.         if ( !Quiet)
  317.             fprintf(stderr, rbmsg, Progname, Nozmodem?"sb":"sz");
  318.         if (c=tryz()) {
  319.             if (c == ZCOMPL)
  320.                 return OK;
  321.             if (c == ERROR)
  322.                 goto fubar;
  323.             c = rzfiles();
  324.             if (c)
  325.                 goto fubar;
  326.         } else {
  327.             for (;;) {
  328.                 if (wcrxpn(secbuf)== ERROR)
  329.                     goto fubar;
  330.                 if (secbuf[0]==0)
  331.                     return OK;
  332.                 if (procheader(secbuf) == ERROR)
  333.                     goto fubar;
  334.                 if (wcrx()==ERROR)
  335.                     goto fubar;
  336.             }
  337.         }
  338.     } else {
  339.         Bytesleft = DEFBYTL; Filemode = 0; Modtime = 0L;
  340.  
  341.         procheader(""); strcpy(Pathname, *argp); checkpath(Pathname);
  342.         fprintf(stderr, "\nrz: ready to receive %s\r\n", Pathname);
  343.         if ((fout=fopen(Pathname, "w")) == NULL)
  344.             return ERROR;
  345.         if (wcrx()==ERROR)
  346.             goto fubar;
  347.     }
  348.     return OK;
  349. fubar:
  350.     canit();
  351.     if (Topipe && fout) {
  352.         pclose(fout);  return ERROR;
  353.     }
  354.     if (fout)
  355.         fclose(fout);
  356.     if (Restricted) {
  357.         unlink(Pathname);
  358.         fprintf(stderr, "\r\nrz: %s removed.\r\n", Pathname);
  359.     }
  360.     return ERROR;
  361. }
  362.  
  363.  
  364. /*
  365.  * Fetch a pathname from the other end as a C ctyle ASCIZ string.
  366.  * Length is indeterminate as long as less than Blklen
  367.  * A null string represents no more files (YMODEM)
  368.  */
  369. wcrxpn(rpn)
  370. char *rpn;    /* receive a pathname */
  371. {
  372.     register c;
  373.  
  374. #ifdef NFGVMIN
  375.     readline(1);
  376. #else
  377.     purgeline();
  378. #endif
  379.  
  380. et_tu:
  381.     Firstsec=TRUE;  Eofseen=FALSE;
  382.     sendline(Crcflg?WANTCRC:NAK);
  383.     Lleft=0;    /* Do read next time ... */
  384.     while ((c = wcgetsec(rpn, 100)) != 0) {
  385.         if (c == WCEOT) {
  386.             zperr( "Pathname fetch returned %d", c);
  387.             sendline(ACK);
  388.             Lleft=0;    /* Do read next time ... */
  389.             readline(1);
  390.             goto et_tu;
  391.         }
  392.         return ERROR;
  393.     }
  394.     sendline(ACK);
  395.     return OK;
  396. }
  397.  
  398. /*
  399.  * Adapted from CMODEM13.C, written by
  400.  * Jack M. Wierda and Roderick W. Hart
  401.  */
  402.  
  403. wcrx()
  404. {
  405.     register int sectnum, sectcurr;
  406.     register char sendchar;
  407.     register char *p;
  408.     int cblklen;            /* bytes to dump this block */
  409.  
  410.     Firstsec=TRUE;sectnum=0; Eofseen=FALSE;
  411.     sendchar=Crcflg?WANTCRC:NAK;
  412.  
  413.     for (;;) {
  414.         sendline(sendchar);    /* send it now, we're ready! */
  415.         Lleft=0;    /* Do read next time ... */
  416.         sectcurr=wcgetsec(secbuf, (sectnum&0177)?50:130);
  417.         report(sectcurr);
  418.         if (sectcurr==(sectnum+1 &Wcsmask)) {
  419.             sectnum++;
  420.             cblklen = Bytesleft>Blklen ? Blklen:Bytesleft;
  421.             if (putsec(secbuf, cblklen)==ERROR)
  422.                 return ERROR;
  423.             if ((Bytesleft-=cblklen) < 0)
  424.                 Bytesleft = 0;
  425.             sendchar=ACK;
  426.         }
  427.         else if (sectcurr==(sectnum&Wcsmask)) {
  428.             zperr( "Received dup Sector");
  429.             sendchar=ACK;
  430.         }
  431.         else if (sectcurr==WCEOT) {
  432.             if (closeit())
  433.                 return ERROR;
  434.             sendline(ACK);
  435.             Lleft=0;    /* Do read next time ... */
  436.             return OK;
  437.         }
  438.         else if (sectcurr==ERROR)
  439.             return ERROR;
  440.         else {
  441.             zperr( "Sync Error");
  442.             return ERROR;
  443.         }
  444.     }
  445. }
  446.  
  447. /*
  448.  * Wcgetsec fetches a Ward Christensen type sector.
  449.  * Returns sector number encountered or ERROR if valid sector not received,
  450.  * or CAN CAN received
  451.  * or WCEOT if eot sector
  452.  * time is timeout for first char, set to 4 seconds thereafter
  453.  ***************** NO ACK IS SENT IF SECTOR IS RECEIVED OK **************
  454.  *    (Caller must do that when he is good and ready to get next sector)
  455.  */
  456.  
  457. wcgetsec(rxbuf, maxtime)
  458. char *rxbuf;
  459. int maxtime;
  460. {
  461.     register checksum, wcj, firstch;
  462.     register unsigned short oldcrc;
  463.     register char *p;
  464.     int sectcurr;
  465.  
  466.     for (Lastrx=errors=0; errors<RETRYMAX; errors++) {
  467.  
  468.         if ((firstch=readline(maxtime))==STX) {
  469.             Blklen=KSIZE; goto get2;
  470.         }
  471.         if (firstch==SOH) {
  472.             Blklen=SECSIZ;
  473. get2:
  474.             sectcurr=readline(1);
  475.             if ((sectcurr+(oldcrc=readline(1)))==Wcsmask) {
  476.                 oldcrc=checksum=0;
  477.                 for (p=rxbuf,wcj=Blklen; --wcj>=0; ) {
  478.                     if ((firstch=readline(1)) < 0)
  479.                         goto bilge;
  480.                     oldcrc=updcrc(firstch, oldcrc);
  481.                     checksum += (*p++ = firstch);
  482.                 }
  483.                 if ((firstch=readline(1)) < 0)
  484.                     goto bilge;
  485.                 if (Crcflg) {
  486.                     oldcrc=updcrc(firstch, oldcrc);
  487.                     if ((firstch=readline(1)) < 0)
  488.                         goto bilge;
  489.                     oldcrc=updcrc(firstch, oldcrc);
  490.                     if (oldcrc & 0xFFFF)
  491.                         zperr( "CRC");
  492.                     else {
  493.                         Firstsec=FALSE;
  494.                         return sectcurr;
  495.                     }
  496.                 }
  497.                 else if (((checksum-firstch)&Wcsmask)==0) {
  498.                     Firstsec=FALSE;
  499.                     return sectcurr;
  500.                 }
  501.                 else
  502.                     zperr( "Checksum");
  503.             }
  504.             else
  505.                 zperr("Sector number garbled");
  506.         }
  507.         /* make sure eot really is eot and not just mixmash */
  508. #ifdef NFGVMIN
  509.         else if (firstch==EOT && readline(1)==TIMEOUT)
  510.             return WCEOT;
  511. #else
  512.         else if (firstch==EOT && Lleft==0)
  513.             return WCEOT;
  514. #endif
  515.         else if (firstch==CAN) {
  516.             if (Lastrx==CAN) {
  517.                 zperr( "Sender CANcelled");
  518.                 return ERROR;
  519.             } else {
  520.                 Lastrx=CAN;
  521.                 continue;
  522.             }
  523.         }
  524.         else if (firstch==TIMEOUT) {
  525.             if (Firstsec)
  526.                 goto humbug;
  527. bilge:
  528.             zperr( "TIMEOUT");
  529.         }
  530.         else
  531.             zperr( "Got 0%o sector header", firstch);
  532.  
  533. humbug:
  534.         Lastrx=0;
  535.         while(readline(1)!=TIMEOUT)
  536.             ;
  537.         if (Firstsec) {
  538.             sendline(Crcflg?WANTCRC:NAK);
  539.             Lleft=0;    /* Do read next time ... */
  540.         } else {
  541.             maxtime=40; sendline(NAK);
  542.             Lleft=0;    /* Do read next time ... */
  543.         }
  544.     }
  545.     /* try to stop the bubble machine. */
  546.     canit();
  547.     return ERROR;
  548. }
  549.  
  550. /*
  551.  * This version of readline is reasoably well suited for
  552.  * reading many characters.
  553.  *  (except, currently, for the Regulus version!)
  554.  *
  555.  * timeout is in tenths of seconds
  556.  */
  557. readline(timeout)
  558. int timeout;
  559. {
  560.     register n;
  561.     static char *cdq;    /* pointer for removing chars from linbuf */
  562.  
  563.     if (--Lleft >= 0) {
  564.         if (Verbose > 8) {
  565.             fprintf(stderr, "%02x ", *cdq&0377);
  566.         }
  567.         return (*cdq++ & Wcsmask);
  568.     }
  569.     n = timeout/10;
  570.     if (n < 2)
  571.         n = 3;
  572.     if (Verbose > 5)
  573.         fprintf(stderr, "Calling read: alarm=%d  Readnum=%d ",
  574.           n, Readnum);
  575.     if (setjmp(tohere)) {
  576. #ifdef TIOCFLUSH
  577. /*        ioctl(iofd, TIOCFLUSH, 0); */
  578. #endif
  579.         Lleft = 0;
  580.         if (Verbose>1)
  581.             fprintf(stderr, "Readline:TIMEOUT\n");
  582.         return TIMEOUT;
  583.     }
  584.     signal(SIGALRM, alrm); alarm(n);
  585.     Lleft=read(iofd, cdq=linbuf, Readnum);
  586.     alarm(0);
  587.     if (Verbose > 5) {
  588.         fprintf(stderr, "Read returned %d bytes\n", Lleft);
  589.     }
  590.     if (Lleft < 1)
  591.         return TIMEOUT;
  592.     --Lleft;
  593.     if (Verbose > 8) {
  594.         fprintf(stderr, "%02x ", *cdq&0377);
  595.     }
  596.     return (*cdq++ & Wcsmask);
  597. }
  598.  
  599.  
  600.  
  601. /*
  602.  * Purge the modem input queue of all characters
  603.  */
  604. purgeline()
  605. {
  606.     Lleft = 0;
  607. #ifdef USG
  608.     ioctl(iofd, TCFLSH, 0);
  609. #else
  610.     lseek(iofd, 0L, 2);
  611. #endif
  612. }
  613.  
  614.  
  615. /*
  616.  * Process incoming file information header
  617.  */
  618. procheader(name)
  619. char *name;
  620. {
  621.     register char *openmode, *p, **pp;
  622.  
  623.     /* set default parameters and overrides */
  624.     openmode = "w";
  625.     Thisbinary = (!Rxascii) || Rxbinary;
  626.     if (Lzmanag)
  627.         zmanag = Lzmanag;
  628.  
  629.     /*
  630.      *  Process ZMODEM remote file management requests
  631.      */
  632.     if (!Rxbinary && zconv == ZCNL)    /* Remote ASCII override */
  633.         Thisbinary = 0;
  634.     if (zconv == ZCBIN)    /* Remote Binary override */
  635.         Thisbinary = TRUE;
  636.     else if (zmanag == ZMAPND)
  637.         openmode = "a";
  638.  
  639. #ifndef BIX
  640.     /* ZMPROT check for existing file */
  641.     if (zmanag == ZMPROT && (fout=fopen(name, "r"))) {
  642.         fclose(fout);  return ERROR;
  643.     }
  644. #endif
  645.  
  646.     Bytesleft = DEFBYTL; Filemode = 0; Modtime = 0L;
  647.  
  648.     p = name + 1 + strlen(name);
  649.     if (*p) {    /* file coming from Unix or DOS system */
  650.         sscanf(p, "%ld%lo%o", &Bytesleft, &Modtime, &Filemode);
  651.         if (Filemode & UNIXFILE)
  652.             ++Thisbinary;
  653.         if (Verbose) {
  654.             fprintf(stderr,  "Incoming: %s %ld %lo %o\n",
  655.               name, Bytesleft, Modtime, Filemode);
  656.         }
  657.     }
  658.  
  659. #ifdef BIX
  660.     if ((fout=fopen("scratchpad", openmode)) == NULL)
  661.         return ERROR;
  662.     return OK;
  663. #else
  664.  
  665.     else {        /* File coming from CP/M system */
  666.         for (p=name; *p; ++p)        /* change / to _ */
  667.             if ( *p == '/')
  668.                 *p = '_';
  669.  
  670.         if ( *--p == '.')        /* zap trailing period */
  671.             *p = 0;
  672.     }
  673.  
  674.     if (!Zmodem && MakeLCPathname && !IsAnyLower(name))
  675.         uncaps(name);
  676.     if (Topipe) {
  677.         sprintf(Pathname, "%s %s", Progname+2, name);
  678.         if (Verbose)
  679.             fprintf(stderr,  "Topipe: %s %s\n",
  680.               Pathname, Thisbinary?"BIN":"ASCII");
  681.         if ((fout=popen(Pathname, "w")) == NULL)
  682.             return ERROR;
  683.     } else {
  684.         strcpy(Pathname, name);
  685.         if (Verbose) {
  686.             fprintf(stderr,  "Receiving %s %s %s\n",
  687.               name, Thisbinary?"BIN":"ASCII", openmode);
  688.         }
  689.         checkpath(name);
  690.         if (Nflag)
  691.             name = "/dev/null";
  692.         if ((fout=fopen(name, openmode)) == NULL)
  693.             return ERROR;
  694.     }
  695.     return OK;
  696. #endif /* BIX */
  697. }
  698.  
  699. /*
  700.  * Putsec writes the n characters of buf to receive file fout.
  701.  *  If not in binary mode, carriage returns, and all characters
  702.  *  starting with CPMEOF are discarded.
  703.  */
  704. putsec(buf, n)
  705. char *buf;
  706. register n;
  707. {
  708.     register char *p;
  709.  
  710.     if (Thisbinary) {
  711.         for (p=buf; --n>=0; )
  712.             putc( *p++, fout);
  713.     }
  714.     else {
  715.         if (Eofseen)
  716.             return OK;
  717.         for (p=buf; --n>=0; ++p ) {
  718.             if ( *p == '\r')
  719.                 continue;
  720.             if (*p == CPMEOF) {
  721.                 Eofseen=TRUE; return OK;
  722.             }
  723.             putc(*p ,fout);
  724.         }
  725.     }
  726.     return OK;
  727. }
  728.  
  729. /*
  730.  *  Send a character to modem.  Small is beautiful.
  731.  */
  732. sendline(c)
  733. {
  734.     char d;
  735.  
  736.     d = c;
  737.     if (Verbose>6)
  738.         fprintf(stderr, "Sendline: %x\n", c);
  739.     write(1, &d, 1);
  740. }
  741.  
  742. xsendline(c)
  743. {
  744.     sendline(c);
  745. }
  746.  
  747. flushmo() {}
  748.  
  749.  
  750.  
  751.  
  752. /* make string s lower case */
  753. uncaps(s)
  754. register char *s;
  755. {
  756.     for ( ; *s; ++s)
  757.         if (isupper(*s))
  758.             *s = tolower(*s);
  759. }
  760. /*
  761.  * IsAnyLower returns TRUE if string s has lower case letters.
  762.  */
  763. IsAnyLower(s)
  764. register char *s;
  765. {
  766.     for ( ; *s; ++s)
  767.         if (islower(*s))
  768.             return TRUE;
  769.     return FALSE;
  770. }
  771.  
  772. /*
  773.  * substr(string, token) searches for token in string s
  774.  * returns pointer to token within string if found, NULL otherwise
  775.  */
  776. char *
  777. substr(s, t)
  778. register char *s,*t;
  779. {
  780.     register char *ss,*tt;
  781.     /* search for first char of token */
  782.     for (ss=s; *s; s++)
  783.         if (*s == *t)
  784.             /* compare token with substring */
  785.             for (ss=s,tt=t; ;) {
  786.                 if (*tt == 0)
  787.                     return s;
  788.                 if (*ss++ != *tt++)
  789.                     break;
  790.             }
  791.     return NULL;
  792. }
  793.  
  794. /*
  795.  * Log an error
  796.  */
  797. /*VARARGS1*/
  798. zperr(s,p,u)
  799. char *s, *p, *u;
  800. {
  801.     if (Verbose <= 0)
  802.         return;
  803.     fprintf(stderr, "Retry %d: ", errors);
  804.     fprintf(stderr, s, p, u);
  805.     fprintf(stderr, "\n");
  806. }
  807.  
  808. /* send cancel string to get the other end to shut up */
  809. canit()
  810. {
  811.     static char canistr[] = {
  812.      24,24,24,24,24,24,24,24,24,24,8,8,8,8,8,8,8,8,8,8,0
  813.     };
  814.  
  815.     printf(canistr);
  816.     Lleft=0;    /* Do read next time ... */
  817.     fflush(stdout);
  818. }
  819.  
  820.  
  821. /*
  822.  * Return 1 iff stdout and stderr are different devices
  823.  *  indicating this program operating with a modem on a
  824.  *  different line
  825.  */
  826. fromcu()
  827. {
  828.     struct stat a, b;
  829.     fstat(1, &a); fstat(2, &b);
  830.     return (a.st_rdev != b.st_rdev);
  831. }
  832.  
  833. report(sct)
  834. int sct;
  835. {
  836.     if (Verbose>1)
  837.         fprintf(stderr,"%03d%c",sct,sct%10? ' ' : '\r');
  838. }
  839.  
  840. /*
  841.  * If called as [-][dir/../]vrzCOMMAND set Verbose to 1
  842.  * If called as [-][dir/../]rzCOMMAND set the pipe flag
  843.  * If called as rb use YMODEM protocol
  844.  */
  845. chkinvok(s)
  846. char *s;
  847. {
  848.     register char *p;
  849.  
  850.     p = s;
  851.     while (*p == '-')
  852.         s = ++p;
  853.     while (*p)
  854.         if (*p++ == '/')
  855.             s = p;
  856.     if (*s == 'v') {
  857.         Verbose=1; ++s;
  858.     }
  859.     Progname = s;
  860.     if (s[0]=='r' && s[1]=='z')
  861.         Batch = TRUE;
  862.     if (s[0]=='r' && s[1]=='b')
  863.         Batch = Nozmodem = TRUE;
  864.     if (s[2] && s[0]=='r' && s[1]=='b')
  865.         Topipe=TRUE;
  866.     if (s[2] && s[0]=='r' && s[1]=='z')
  867.         Topipe=TRUE;
  868. }
  869.  
  870. /*
  871.  * Totalitarian Communist pathname processing
  872.  */
  873. checkpath(name)
  874. char *name;
  875. {
  876.     if (Restricted) {
  877.         if (fopen(name, "r") != NULL) {
  878.             canit();
  879.             fprintf(stderr, "\r\nrz: %s exists\n", name);
  880.             bibi(-1);
  881.         }
  882.         /* restrict pathnames to current tree or uucppublic */
  883.         if ( substr(name, "../")
  884.          || (name[0]== '/' && strncmp(name, PUBDIR, strlen(PUBDIR))) ) {
  885.             canit();
  886.             fprintf(stderr,"\r\nrz:\tSecurity Violation\r\n");
  887.             bibi(-1);
  888.         }
  889.     }
  890. }
  891.  
  892. /*
  893.  * Initialize for Zmodem receive attempt, try to activate Zmodem sender
  894.  *  Handles ZSINIT frame
  895.  *  Return ZFILE if Zmodem filename received, -1 on error,
  896.  *   ZCOMPL if transaction finished,  else 0
  897.  */
  898. tryz()
  899. {
  900.     register c, n;
  901.     register cmdzack1flg;
  902.  
  903.     if (Nozmodem)        /* Check for "rb" program name */
  904.         return 0;
  905.  
  906.  
  907.     for (n=Zmodem?15:5; --n>=0; ) {
  908.         /* Set buffer length (0) and capability flags */
  909.         stohdr(0L);
  910. #ifdef CANBREAK
  911.         Txhdr[ZF0] = CANFC32|CANFDX|CANOVIO|CANBRK;
  912. #else
  913.         Txhdr[ZF0] = CANFC32|CANFDX|CANOVIO;
  914. #endif
  915.         if (Zctlesc)
  916.             Txhdr[ZF0] |= TESCCTL;
  917.         zshhdr(tryzhdrtype, Txhdr);
  918.         if (tryzhdrtype == ZSKIP)    /* Don't skip too far */
  919.             tryzhdrtype = ZRINIT;    /* CAF 8-21-87 */
  920. again:
  921.         switch (zgethdr(Rxhdr, 0)) {
  922.         case ZRQINIT:
  923.             continue;
  924.         case ZEOF:
  925.             continue;
  926.         case TIMEOUT:
  927.             continue;
  928.         case ZFILE:
  929.             zconv = Rxhdr[ZF0];
  930.             zmanag = Rxhdr[ZF1];
  931.             ztrans = Rxhdr[ZF2];
  932.             tryzhdrtype = ZRINIT;
  933.             c = zrdata(secbuf, KSIZE);
  934.             mode(3);
  935.             if (c == GOTCRCW)
  936.                 return ZFILE;
  937.             zshhdr(ZNAK, Txhdr);
  938.             goto again;
  939.         case ZSINIT:
  940.             Zctlesc = TESCCTL & Rxhdr[ZF0];
  941.             if (zrdata(Attn, ZATTNLEN) == GOTCRCW) {
  942.                 zshhdr(ZACK, Txhdr);
  943.                 goto again;
  944.             }
  945.             zshhdr(ZNAK, Txhdr);
  946.             goto again;
  947.         case ZFREECNT:
  948.             stohdr(getfree());
  949.             zshhdr(ZACK, Txhdr);
  950.             goto again;
  951.         case ZCOMMAND:
  952.             cmdzack1flg = Rxhdr[ZF0];
  953.             if (zrdata(secbuf, KSIZE) == GOTCRCW) {
  954.                 if (cmdzack1flg & ZCACK1)
  955.                     stohdr(0L);
  956.                 else
  957.                     stohdr((long)sys2(secbuf));
  958.                 purgeline();    /* dump impatient questions */
  959.                 do {
  960.                     zshhdr(ZCOMPL, Txhdr);
  961.                 }
  962.                 while (++errors<20 && zgethdr(Rxhdr,1) != ZFIN);
  963.                 ackbibi();
  964.                 if (cmdzack1flg & ZCACK1)
  965.                     exec2(secbuf);
  966.                 return ZCOMPL;
  967.             }
  968.             zshhdr(ZNAK, Txhdr); goto again;
  969.         case ZCOMPL:
  970.             goto again;
  971.         default:
  972.             continue;
  973.         case ZFIN:
  974.             ackbibi(); return ZCOMPL;
  975.         case ZCAN:
  976.             return ERROR;
  977.         }
  978.     }
  979.     return 0;
  980. }
  981.  
  982. /*
  983.  * Receive 1 or more files with ZMODEM protocol
  984.  */
  985. rzfiles()
  986. {
  987.     register c;
  988.  
  989.     for (;;) {
  990.         switch (c = rzfile()) {
  991.         case ZEOF:
  992.         case ZSKIP:
  993.             switch (tryz()) {
  994.             case ZCOMPL:
  995.                 return OK;
  996.             default:
  997.                 return ERROR;
  998.             case ZFILE:
  999.                 break;
  1000.             }
  1001.             continue;
  1002.         default:
  1003.             return c;
  1004.         case ERROR:
  1005.             return ERROR;
  1006.         }
  1007.     }
  1008. }
  1009.  
  1010. /*
  1011.  * Receive a file with ZMODEM protocol
  1012.  *  Assumes file name frame is in secbuf
  1013.  */
  1014. rzfile()
  1015. {
  1016.     register c, n;
  1017.     long rxbytes;
  1018.  
  1019.     Eofseen=FALSE;
  1020.     if (procheader(secbuf) == ERROR) {
  1021.         return (tryzhdrtype = ZSKIP);
  1022.     }
  1023.  
  1024.     n = 20; rxbytes = 0l;
  1025.  
  1026.     for (;;) {
  1027.         stohdr(rxbytes);
  1028.         zshhdr(ZRPOS, Txhdr);
  1029. nxthdr:
  1030.         switch (c = zgethdr(Rxhdr, 0)) {
  1031.         default:
  1032.             vfile("rzfile: zgethdr returned %d", c);
  1033.             return ERROR;
  1034.         case ZNAK:
  1035.         case TIMEOUT:
  1036.             if ( --n < 0) {
  1037.                 vfile("rzfile: zgethdr returned %d", c);
  1038.                 return ERROR;
  1039.             }
  1040.         case ZFILE:
  1041.             zrdata(secbuf, KSIZE);
  1042.             continue;
  1043.         case ZEOF:
  1044.             if (rclhdr(Rxhdr) != rxbytes) {
  1045.                 /*
  1046.                  * Ignore eof if it's at wrong place - force
  1047.                  *  a timeout because the eof might have gone
  1048.                  *  out before we sent our zrpos.
  1049.                  */
  1050.                 errors = 0;  goto nxthdr;
  1051.             }
  1052.             if (closeit()) {
  1053.                 tryzhdrtype = ZFERR;
  1054.                 vfile("rzfile: closeit returned <> 0");
  1055.                 return ERROR;
  1056.             }
  1057.             vfile("rzfile: normal EOF");
  1058.             return c;
  1059.         case ERROR:    /* Too much garbage in header search error */
  1060.             if ( --n < 0) {
  1061.                 vfile("rzfile: zgethdr returned %d", c);
  1062.                 return ERROR;
  1063.             }
  1064.             zmputs(Attn);
  1065.             continue;
  1066.         case ZDATA:
  1067.             if (rclhdr(Rxhdr) != rxbytes) {
  1068.                 if ( --n < 0) {
  1069.                     return ERROR;
  1070.                 }
  1071.                 zmputs(Attn);  continue;
  1072.             }
  1073. moredata:
  1074.             if (Verbose>1)
  1075.                 fprintf(stderr, "\r%7ld ZMODEM%s    ",
  1076.                   rxbytes, Crc32?" CRC-32":"");
  1077.             switch (c = zrdata(secbuf, KSIZE)) {
  1078.             case ZCAN:
  1079.                 vfile("rzfile: zgethdr returned %d", c);
  1080.                 return ERROR;
  1081.             case ERROR:    /* CRC error */
  1082.                 if ( --n < 0) {
  1083.                     vfile("rzfile: zgethdr returned %d", c);
  1084.                     return ERROR;
  1085.                 }
  1086.                 zmputs(Attn);
  1087.                 continue;
  1088.             case TIMEOUT:
  1089.                 if ( --n < 0) {
  1090.                     vfile("rzfile: zgethdr returned %d", c);
  1091.                     return ERROR;
  1092.                 }
  1093.                 continue;
  1094.             case GOTCRCW:
  1095.                 n = 20;
  1096.                 putsec(secbuf, Rxcount);
  1097.                 rxbytes += Rxcount;
  1098.                 stohdr(rxbytes);
  1099.                 zshhdr(ZACK, Txhdr);
  1100.                 sendline(XON);
  1101.                 goto nxthdr;
  1102.             case GOTCRCQ:
  1103.                 n = 20;
  1104.                 putsec(secbuf, Rxcount);
  1105.                 rxbytes += Rxcount;
  1106.                 stohdr(rxbytes);
  1107.                 zshhdr(ZACK, Txhdr);
  1108.                 goto moredata;
  1109.             case GOTCRCG:
  1110.                 n = 20;
  1111.                 putsec(secbuf, Rxcount);
  1112.                 rxbytes += Rxcount;
  1113.                 goto moredata;
  1114.             case GOTCRCE:
  1115.                 n = 20;
  1116.                 putsec(secbuf, Rxcount);
  1117.                 rxbytes += Rxcount;
  1118.                 goto nxthdr;
  1119.             }
  1120.         }
  1121.     }
  1122. }
  1123.  
  1124. /*
  1125.  * Send a string to the modem, processing for \336 (sleep 1 sec)
  1126.  *   and \335 (break signal)
  1127.  */
  1128. zmputs(s)
  1129. char *s;
  1130. {
  1131.     register c;
  1132.  
  1133.     while (*s) {
  1134.         switch (c = *s++) {
  1135.         case '\336':
  1136.             sleep(1); continue;
  1137.         case '\335':
  1138.             sendbrk(); continue;
  1139.         default:
  1140.             sendline(c);
  1141.         }
  1142.     }
  1143. }
  1144.  
  1145. /*
  1146.  * Close the receive dataset, return OK or ERROR
  1147.  */
  1148. closeit()
  1149. {
  1150.     if (Topipe) {
  1151.         if (pclose(fout)) {
  1152.             return ERROR;
  1153.         }
  1154.         return OK;
  1155.     }
  1156.     if (fclose(fout)==ERROR) {
  1157.         fprintf(stderr, "file close ERROR\n");
  1158.         return ERROR;
  1159.     }
  1160.     if (Modtime) {
  1161.         timep[0] = time(NULL);
  1162.         timep[1] = Modtime;
  1163.         utime(Pathname, timep);
  1164.     }
  1165.     if (Filemode)
  1166.         chmod(Pathname, (07777 & Filemode));
  1167.     return OK;
  1168. }
  1169.  
  1170. /*
  1171.  * Ack a ZFIN packet, let byegones be byegones
  1172.  */
  1173. ackbibi()
  1174. {
  1175.     register n;
  1176.  
  1177.     vfile("ackbibi:");
  1178.     Readnum = 1;
  1179.     stohdr(0L);
  1180.     for (n=3; --n>=0; ) {
  1181.         purgeline();
  1182.         zshhdr(ZFIN, Txhdr);
  1183.         switch (readline(100)) {
  1184.         case 'O':
  1185.             readline(1);    /* Discard 2nd 'O' */
  1186.             vfile("ackbibi complete");
  1187.             return;
  1188.         case RCDO:
  1189.             return;
  1190.         case TIMEOUT:
  1191.         default:
  1192.             break;
  1193.         }
  1194.     }
  1195. }
  1196.  
  1197.  
  1198.  
  1199. /*
  1200.  * Local console output simulation
  1201.  */
  1202. bttyout(c)
  1203. {
  1204.     if (Verbose || fromcu())
  1205.         putc(c, stderr);
  1206. }
  1207.  
  1208. /*
  1209.  * Strip leading ! if present, do shell escape. 
  1210.  */
  1211. sys2(s)
  1212. register char *s;
  1213. {
  1214.     if (*s == '!')
  1215.         ++s;
  1216.     return system(s);
  1217. }
  1218. /*
  1219.  * Strip leading ! if present, do exec.
  1220.  */
  1221. exec2(s)
  1222. register char *s;
  1223. {
  1224.     if (*s == '!')
  1225.         ++s;
  1226.     mode(0);
  1227.     execl("/bin/sh", "sh", "-c", s);
  1228. }
  1229. /* End of rz.c */
  1230.